/* CCScript values */
#pragma once

#define String ByteChunk

// Forward declarations
class Table;
class Function;
class String;
class Label;

#include <stdio.h>

// This class defines a "value" in CCScript.
//
// A value can be one of the following:
// - a number
// - a string (by reference)
// - a function (by reference)
// - a table (by reference)
// - a label reference
//
// The first four types are straightforward; the last is a special type
// that is essentially an integer whose value is unknown until the final
// pass of compilation, and is thus somewhat restricted in its use.
//
// A value contains its type, and the actual value. Values can be returned
// from expressions and assigned to variables. Values can also be output
// into the ROM by explicit or implicit output statements.
//


// Type is just a wrapper around an enum
struct Type
{
	enum EType {
		Number,
		String,
		Function,
		Table,
		Label,
		Undefined
	};

	// Construction and assignment
	Type();
	Type(EType t);
	Type(const Type& other);
	Type& operator=(const Type& rhs);
	Type& operator=(Type::EType rhs);

	// Comparison
	bool operator==(const Type& rhs) const;
	bool operator!=(const Type& rhs) const;
	bool operator==(Type::EType rhs) const;
	bool operator!=(Type::EType rhs) const;

	// Implicit conversion
	operator EType();

private:
	EType t;
};



struct Value
{
	// Okay, good question here: are these pointers aliasing or owning?
	// Aliasing, obviously, since a value containing a pointer is a
	// reference, and thus other values might be equal, that is, share
	// a reference to the same object.
	// However, is the use of naked pointers here adequate, or should we
	// use a reference-counting pointer like boost::shared_ptr?

	// Note that in the case of the Label* pointer, using boost::shared_ptr
	// would have significant ramifications, since Label* pointers are
	// currently just pointers to AST nodes, are all allocated during AST
	// generation.

	// All the more reason to finally separate Label* into a separate
	// class... the original compiler framework was built without too much
	// regard for proper C++ memory management; it's probably fairly leaky
	// at any rate, and it doesn't use any sort of smart pointers.

	// Incorporating smart pointers might be a good idea as long as we're
	// refactoring stuff; we just need to make sure we're carefully considering
	// the semantics of all the pointers we're using.

	// We should consider the pointer semantics within the AST first, since
	// that's really at the center of everything in this compiler. It's sort
	// of a monolithic structure, actually, that hopefully we will be able to
	// make a bit less monolithic.

	// Anyway, each AST node can be said to "own" the pointers to its subnodes,
	// and since the entire AST is retained until the end of evaluation, we
	// don't need to worry about anything else during evaluation aliasing these
	// pointers.

	// Other things, though, like _values_, might be more short-lived, so we
	// should keep these things distinct from the AST. 

	// NOTE: Will we need to reference count our references? I think we probably
	// should... Yeah.

	// A value _must_ own its pointers -- it also needs to keep a reference count,
	// since it may be copied to another value, which must then "share" ownership.
	// So, if you construct a Value by passing it a pointer to a string, function,
	// or table, it now "owns" that pointer and you should under no circumstance
	// delete the pointer or give it to anything else.

	// In fact, for this reason I think the pointers themselves should be private
	// data. If something else needs to hold a reference to them, it can get a
	// weak pointer, perhaps.

	// Value constructors
	Value();
	Value(int number);
	Value(String* string);
	Value(Function* function);
	Value(Table* table);
	Value(Label* label);

	virtual ~Value();

	// Copy constructor
	Value(const Value& other);

	// Assignment
	Value& operator=(const Value& rhs);

	// Properties and methods
	bool IsRefCounted();			// Returns true if this value is of a ref-counted type
	void Release();					// Release reference to ref-counted type

private:
	Type type;
	union UVal {
		int number;
		String* string;
		Table* table;
		Function* function;
		Label* label;

		UVal() { }
		UVal(int n) { number = n; }
		UVal(String* s) { string = s; }
		UVal(Table* t) { table = t; }
		UVal(Function* f) { function = f; }
		UVal(Label* l) { label = l; }
	} val;
	int* refcount;	// for strings, functions, tables
	void Delete();	// deletes ref-counted pointer

	// Hmm. Should we keep the reference count right here in value, or should
	// we simply have value store reference-counting pointer objects? One consequence
	// of using a ref-counting pointer is that we couldn't as easily use a union
	// here, since union members cannot have destructors.

	// On the other hand, using the union means we need to carefully select which
	// union member to delete based on the value's type.

	// If we dispensed with the union and just had a set of smart pointers (of
	// which we'd only use one at a time), we wouldn't have to worry about choosing
	// which one to delete -- they'd all be destroyed together, and whichever one
	// contained a valid pointer would delete it.

	// However, then we'd have to worry about which pointer to _copy_...

	// Eh, we'll just use the union and keep reference counting in Value now.
	// It won't be too hard to factor it out later if that turns out to be better.
};

